//
// Created by Administrator on 2024/7/10.
//

#ifndef JTT809_PACKET_H
#define JTT809_PACKET_H
#include <boost/crc.hpp>

#include "jtt809_config.h"
#include "jtt809_body.h"
#include "jtt809_header.h"
#include "../jtt809_logger.h"
#include "msgbody/message_body_factory.h"
#include "msgpack/MessagePackWriter.h"
#include "msgpack/messagepack_reader.h"

namespace protocol {
    class JTT809Packet final : public Serializable, public Sizeable {
        uint8_t beginFlag;
        JTT809Header header;
        std::shared_ptr<JTT809Body> body;
        uint16_t crcCode;
        uint8_t endFlag;

        Logger logger = JTT809Logger::logger("JTT809Packet");
        bool selfManage = false;

    public:
        [[nodiscard]] uint8_t get_begin_flag() const {
            return beginFlag;
        }

        [[nodiscard]] const JTT809Header & get_header() const {
            return header;
        }

        [[nodiscard]] JTT809Body * get_body() const {
            return body.get();
        }

        [[nodiscard]] uint16_t get_crc_code() const {
            return crcCode;
        }

        [[nodiscard]] uint8_t get_end_flag() const {
            return endFlag;
        }

        JTT809Packet();

        JTT809Packet(MessagePackReader &reader) : beginFlag(0x5b), endFlag(0x5d) {
            deserialize(reader);
        }

        JTT809Packet(const JTT809Header &header, std::shared_ptr<JTT809Body> body): beginFlag(0x5b), header(header), body(body), crcCode(0),
                                                          endFlag(0x5d) {
        }

        ~JTT809Packet() override {

        }

        void serialize(MessagePackWriter& writer) override {
            writer.writeByte(beginFlag, false);
            auto serializeWriter = MessagePackWriter(2048);
            header.serialize(serializeWriter);
            if (uint32_t encrypt_key; header.tryGetEncryptKey(encrypt_key)) {
                auto body_writer = MessagePackWriter(1024);
                body->serialize(body_writer);
                const auto buf = body_writer.flushAndGet();
                const auto encrypted = encrypt(reinterpret_cast<unsigned char*>(buf.get()), body_writer.size(),encrypt_key, configs::option);
                serializeWriter.writeBytes(encrypted, body_writer.size());
            } else
                body->serialize(serializeWriter);
            writer.combine(serializeWriter);
            writer.writeCrcCodeAndEndFlag(endFlag);
        }

        void initBody() {
            selfManage = true;
            const auto code = static_cast<JTT809Bodies>(header.get_msg_id());
            body = std::shared_ptr<JTT809Body>(MessageBodyFactory::createBody(code));
        }

        void deserialize(MessagePackReader& reader) override {
            reader.validate();
            uint8_t begin;
            reader.readByte(begin);
            if (begin != beginFlag) {
                ERR(logger,"Wrong begin flag which is 0x{}", toHex(&begin));
                ENABLE_STACKTRACE
                throw JTT809Exception(JTT809ErrorCode::WrongFlag, "Wrong begin flag");
            }
            header.deserialize(reader);
            if (uint32_t encrypt_key; header.tryGetEncryptKey(encrypt_key)) {
                reader.decryptPart(header.get_msg_length()-NO_BODY_LENGTH, encrypt_key, configs::option);
            }
            initBody();
            body->deserialize(reader);
            reader.readUint(crcCode);
            uint8_t end;
            reader.readByte(end);
            if (end != endFlag) {
                ERR(logger,"Wrong end flag which is 0x{}", toHex(&end));
                ENABLE_STACKTRACE
                throw JTT809Exception(JTT809ErrorCode::WrongFlag, "Wrong end flag");
            }
        }

    protected:
        [[nodiscard]] size_t _size_() const override {
            return (size_of(beginFlag) + size_of(header) + size_of(*body) + size_of(crcCode) + size_of(endFlag));
        }
    };
}

#endif //JTT809_PACKET_H
